//#######################################################################################################
//#################################### Plugin-100: NRF24L01 plugin ######################################
//#######################################################################################################

/*********************************************************************************************\
 * This protocol provides communication with the NRF24L01 2.4 GHz Transceiver
 * 
 * Author             : Martinus van den Broek
 * Support            : GEEN, PRIVE PLUGIN!
 * Date               : 27 Sep 2014 (removed some debugging code to reduce code, delay/checkonline after channel request)
 * Version            : 0.9
 * Compatibility      : R744
 * Syntax             :
 *                      nrf rf,<channel>	set RF channel (1-125, 0 = auto find channel)
 *                      nrf debug,on/off	turn debugging on/off (mega only)
 *                      nrf delay,<ms>		interpacket send delay in msec
 *                      nrf sendto,<unit> 	set target unit for single peer communication
 *                      nrf status,<unit>	check connectivity/retries
 *                      nrf reset		init device
 \*********************************************************************************************/
#ifndef NRF_CSN_PIN
  #if NODO_MEGA
    #define NRF_CSN_PIN  40
    #define NRF_CE_PIN   41
  #else
    #define NRF_CSN_PIN   7
    #define NRF_CE_PIN    8
  #endif
#endif

#ifndef NRF_MOSI_PIN
  #if NODO_MEGA
    #define NRF_MOSI_PIN 51  // hardware-spi pin = 51
    #define NRF_MISO_PIN 50  // hardware-spi pin = 50
    #define NRF_SCK_PIN  52  // hardware-spi pin = 52
  #else
    #define NRF_MOSI_PIN 11  // hardware-spi pin = 11
    #define NRF_MISO_PIN 12  // hardware-spi pin = 12
    #define NRF_SCK_PIN  13  // hardware-spi pin = 13
  #endif
#endif

#ifndef NRF_ADDRESS
  #define NRF_ADDRESS               2,3,4,5 // last 4 bytes of address, 1st byte is controlled by plugin
#endif
#ifndef NRF_CHANNEL
  #if NODO_MEGA
    #define NRF_CHANNEL               36 // Mega defaults to channel 36
  #else
    #define NRF_CHANNEL               0 // Small defaults to 0 (run findmaster)
  #endif
#endif
#ifndef NRF_SOFT_SPI
  #define NRF_SOFT_SPI              false
#endif
#ifndef NRF_SKIP_CE_PIN
  #define NRF_SKIP_CE_PIN           false
#endif

#ifndef NRF_DEBUG
  #if NODO_MEGA
    #define NRF_DEBUG true
  #else
    #define NRF_DEBUG false
  #endif
#endif

#define PLUGIN_ID 100
#define PLUGIN_NAME_100 "NRF"

#define NRF_PAYLOAD_SIZE	  32
#define NRF_UNIT_MAX              32

#define NRF_PAYLOAD_NODO           0
#define NRF_PAYLOAD_PINGREQ        4
#define NRF_PAYLOAD_PINGREP        5
#define NRF_PAYLOAD_CHANNEL      254
#define NRF_PAYLOAD_ONLINE       255

#define NRF_EN_AA       0x01		// Register for Enable Auto Acknowledge configuration register
#define NRF_RF_SETUP    0x06		// Register for Set Data Rate
#define NRF_SETUP_RETR  0x04		// Register for Retry delay and count
#define NRF_EN_RXADDR   0x02		// Register for RX enable receive pipes

#ifdef PLUGIN_100_CORE

  #define mirf_ADDR_LEN	5
  #define mirf_CONFIG ((1<<EN_CRC) | (0<<CRCO) )

  #if NRF_SOFT_SPI
    void SPI_begin();
    unsigned char SPI_transfer(unsigned char Byte);
  #endif

  // NRF function prototypes
  void Nrf24_setChannel(byte channel);
  void Nrf24_init();
  void Nrf24_config();
  void Nrf24_send(uint8_t *value);
  void Nrf24_setRADDR(uint8_t * adr);
  void Nrf24_setTADDR(uint8_t * adr);
  bool Nrf24_dataReady();
  bool Nrf24_isSending();
  bool Nrf24_rxFifoEmpty();
  bool Nrf24_txFifoEmpty();
  void Nrf24_getData(uint8_t * data);
  uint8_t Nrf24_getStatus();
  void Nrf24_transmitSync(uint8_t *dataout,uint8_t len);
  void Nrf24_transferSync(uint8_t *dataout,uint8_t *datain,uint8_t len);
  void Nrf24_configRegister(uint8_t reg, uint8_t value);
  void Nrf24_readRegister(uint8_t reg, uint8_t * value, uint8_t len);
  void Nrf24_writeRegister(uint8_t reg, uint8_t * value, uint8_t len);
  void Nrf24_powerUpRx();
  void Nrf24_powerUpTx();
  void Nrf24_powerDown();
  void Nrf24_csnHi();
  void Nrf24_csnLow();
  void Nrf24_ceHi();
  void Nrf24_ceLow();
  void Nrf24_flushRx();

  // NRF24 Variables
  uint8_t Nrf24_PTX;
  uint8_t Nrf24_channel=NRF_CHANNEL;

  // Plugin function prototypes
  boolean NRF_init(void);
  byte NRF_findMaster();
  void NRF_changeChannel(byte channel);
  boolean NRF_receive(struct NodoEventStruct *event);
  void SendSelectedRF(struct NodoEventStruct *ES);
  void NRF_send(struct NodoEventStruct *event);
  void ethershield_off(void);
  void ethershield_on(void);
  void NRF_CheckOnline();
  byte NRF_sendpacket(byte Source, byte Destination, byte ID, byte Size);

  // Plugin variables
  boolean NRFOnline[NRF_UNIT_MAX+1];
  byte NRF_single_target=0;				// store destination in case of single peer mode
  byte NRF_forceSingleTarget=0;				// override all basic logic, always send to this peer only
  byte NRF_address[5] = { 1,NRF_ADDRESS };
  byte NRF_status=0;
  boolean NRF_live=false;
  boolean NRF_debug = false;
  int NRF_sendDelay = 0;

#endif

struct NRFPayloadStruct
{
  byte Source;
  byte Destination;
  byte ID;
  byte Size;
  byte Data[28];
}
NRFPayload;

boolean Plugin_100(byte function, struct NodoEventStruct *event, char *string)
{
  boolean success=false;

  switch(function)
  {
#if defined(PLUGIN_100_CORE) && !defined(NODO_TINY)
case PLUGIN_INIT:
    {
      ethershield_off();
      #if NRF_DEBUG
        Serial.println("NRF Init!");
      #endif
      NRF_live=NRF_init();
      if (Nrf24_channel==0) NRF_findMaster();
      NRF_CheckOnline();
      ethershield_on();
      break;
    }

  case PLUGIN_COMMAND:
    {
      ethershield_off();
      switch(event->Par1)
        {
          case CMD_RESET:
            {
              NRF_live=NRF_init();
              NRF_CheckOnline();
              break;
            }

          case VALUE_SOURCE_RF:							// RF
            {
              if (event->Par2 == 0) NRF_findMaster();
              if ((event->Par2 >= 1) && (event->Par2 <= 62))			// Select channel between 1 <> 62
                {
                  Nrf24_channel=event->Par2;
                  #if NODO_MEGA
                    NRF_changeChannel(Nrf24_channel);                             // broadcast change to all channels!
                  #endif
                  Nrf24_setChannel(Nrf24_channel);
                  NRF_CheckOnline();						// Send data on air to find out who's online
                }
              break;
            }

          case CMD_STATUS:							// ping other peer
            {
              unsigned long NRF_roundtrip=millis();
              NRF_status=0;
              NRF_sendpacket(Settings.Unit, event->Par2, NRF_PAYLOAD_PINGREQ, 0);
              #if NODO_MEGA
                Serial.print("status ");
                Serial.println((int)NRF_status);
                Serial.print("roundtrip ");
                Serial.println(millis()-NRF_roundtrip);
              #endif
              byte NRF_retries=0;
              Nrf24_readRegister(8, &NRF_retries, sizeof(NRF_retries) );
              #if NODO_MEGA
                Serial.print("retries ");
              #endif
              Serial.println((int)(NRF_retries & 0x0f));
              break;
            }

          case CMD_DELAY:							// set send delay after each packet, default = 0 ms
            {
              NRF_sendDelay = event->Par2;
              break;
            }

          case CMD_SENDTO:							// force single peer mode, only send to this peer (central Nodo unit)
            {
              NRF_forceSingleTarget = event->Par2;
              break;
            }

          #if NRF_DEBUG      
          case CMD_DEBUG:							// Turn debugging output on/off
            {
              if (event->Par2 == VALUE_OFF) NRF_debug = false;
              if (event->Par2 == VALUE_ON) NRF_debug = true;
              break;
            }
          #endif

        } //end case

      ethershield_on();
      success=true;
      break;
    } // case

#endif // PLUGIN_100_CORE
    #if NODO_MEGA || NODO_MINIMMI // alleen relevant voor een Nodo Mega of custom Small met MINI MMI
    case PLUGIN_MMI_IN:
      {
      char *TempStr=(char*)malloc(26);
      string[25]=0;

      if(GetArgv(string,TempStr,1))
        {
        if(strcasecmp(TempStr,PLUGIN_NAME_100)==0)
          {
          if(GetArgv(string,TempStr,2)) 
            {
            if(GetArgv(string,TempStr,3))
              {
                event->Type = NODO_TYPE_PLUGIN_COMMAND;
                event->Command = PLUGIN_ID; // Plugin nummer  
                success=true;
              }
            }
          }
        }
      free(TempStr);
      break;
      }

    case PLUGIN_MMI_OUT:
      {
      strcpy(string,PLUGIN_NAME_100);
      strcat(string," ");
      strcat(string,cmd2str(event->Par1));
      strcat(string,",");
      strcat(string,int2str(event->Par2));
      break;
      }
    #endif //MMI
  }      
  return success;
}

#ifdef PLUGIN_100_CORE

boolean NRF_init(void)
{
  #if NRF_SOFT_SPI
    SPI_begin();
    #ifdef NODO_TINY
      delay(100); // needed in some hard boot situations ?
    #endif
  #else
    SPI.begin();
    SPI.setDataMode(SPI_MODE0);
    SPI.setClockDivider(SPI_2XCLOCK_MASK);
  #endif
  Nrf24_init();

  byte check=0;
  Nrf24_readRegister(0,&check,1);
  Serial.println((int)check);
  if (check == 0xff || check == 0x0)
    {
      #if NRF_DEBUG      
        for(byte x=0; x < 10; x++)
        {
          digitalWrite(PIN_LED_RGB_R,HIGH);
          delay(50);
          digitalWrite(PIN_LED_RGB_R,LOW);
          delay(50);
        } 
      #endif
      return false;
    }

  NRF_address[0]=Settings.Unit;
  Nrf24_setRADDR((byte *)NRF_address);
  Nrf24_config();

  byte rf_setup=0x27;								// 250 kbps, max TX power, set bit 0 high for SI24R1 +7db
  Nrf24_writeRegister(NRF_RF_SETUP, &rf_setup, sizeof(rf_setup) );
  byte en_aa=0x3F;								// Auto-Ack Enabled
  Nrf24_writeRegister(NRF_EN_AA, &en_aa, sizeof(en_aa) );
  byte setup_retr=0xff;								// delay 4000uS, 15 retries
  Nrf24_writeRegister(NRF_SETUP_RETR, &setup_retr, sizeof(setup_retr) );
  byte en_rxaddr=0x7;								// enable RX pipes 0,1,2
  Nrf24_writeRegister(NRF_EN_RXADDR, &en_rxaddr, sizeof(en_rxaddr) );
  Nrf24_flushRx();

  return true;
}

byte NRF_findMaster()
{
  byte channel=2;
  byte found=0;
  Nrf24_flushRx();
  while(found < 5 && channel <63)
    {
      #if NRF_DEBUG      
        #ifdef NODO_TINY
          SoftwareSerial_print(int2str(channel));
        #else
          Serial.print((int)channel);
        #endif
      #endif
      Nrf24_setChannel(channel);
      if ((NRF_sendpacket(Settings.Unit, 255, NRF_PAYLOAD_ONLINE, 0) & 0x20) == 0x20)
        found++;
      else
        {
          found=0;
          channel++;
        }
      #if NRF_DEBUG      
        #ifdef NODO_TINY
          SoftwareSerial_print("-");
          SoftwareSerial_println(int2str(NRF_status));
        #else
          Serial.print("-");
          Serial.println((int)NRF_status);
        #endif
      #endif
   }
  Serial.println((int)channel);
  return channel;
}

void NRF_changeChannel(byte channel)
{
  NRFPayload.Data[0]=channel;
  for (byte x=1; x <63; x++)
    {
      #ifdef NODO_TINY
        SoftwareSerial_println(int2str(x));
      #else
        Serial.println((int)x);
      #endif
      Nrf24_setChannel(x);
      NRF_sendpacket(Settings.Unit, 255, NRF_PAYLOAD_CHANNEL, 1);
   }
}

boolean NRF_receive(struct NodoEventStruct *event)
{
  if (!NRF_live) return false;

  boolean dataready=false;
  
  ethershield_off();
  if(!Nrf24_isSending() && Nrf24_dataReady())
    {
      Nrf24_getData((byte *)&NRFPayload);

      #if NRF_DEBUG      
      if (NRF_debug)
        {
          Serial.print("NRF RX ");
          Serial.print("S:");
          Serial.print((int)NRFPayload.Source);
          Serial.print(", D:");
          Serial.print((int)NRFPayload.Destination);
          Serial.print(", ID:");
          Serial.print((int)NRFPayload.ID);
          Serial.print(", SZ:");
          Serial.println((int)NRFPayload.Size);
        }
      #endif
          
      if (NRFPayload.ID==NRF_PAYLOAD_NODO)
        {
          memcpy((byte*)event, (byte*)&NRFPayload+4,sizeof(struct NodoEventStruct));
          event->Port=VALUE_SOURCE_RF;
          event->Direction=VALUE_DIRECTION_INPUT;
          
          // this code is to speed up sendto eventlistshow
          // test stuff, remember peer if sendto request reveived
          if ((event->Type & NODO_TYPE_SYSTEM) && (event->Command & SYSTEM_COMMAND_QUEUE_SENDTO))
            NRF_single_target = event->SourceUnit;
          // and reset if sendto transmissions are finished
          
          dataready=true;

          #if NRF_DEBUG 
          if (NRF_debug)
            {
              Serial.print("T ");
              Serial.print((int)event->Type);
              Serial.print(" S ");
              Serial.print((int)event->SourceUnit);
              Serial.print(" D ");
              Serial.print((int)event->DestinationUnit);
              Serial.print(" C ");
              Serial.print((int)event->Command);
               Serial.print(" F ");
              Serial.print((int)event->Flags);
              Serial.print(" PT ");
              Serial.println((int)NRF_single_target);
            }
          #endif
        }

      if (NRFPayload.ID==NRF_PAYLOAD_CHANNEL)
        {
          Nrf24_channel = NRFPayload.Data[0];
          Nrf24_setChannel(Nrf24_channel);
          for (byte x=1; x < Settings.Unit+2; x++)
            {
              delay(3000);
              Nrf24_flushRx();
            }
          NRF_CheckOnline();						// Send data on air to find out who's online
          #if NRF_DEBUG 
          if (NRF_debug)
            {
              Serial.println(Nrf24_channel);
            }
          #endif
        }
    }
  ethershield_on();
  return dataready;
}


void SendSelectedRF(struct NodoEventStruct *ES)
{
  #if NODO_MEGA
  if(Transmission_SendToUnit!=Settings.Unit && Transmission_SendToUnit!=0)
    { // "SendTo" is active, select between 433M or 2.4G radio
     if(NRFOnline[Transmission_SendToUnit]) // this Nodo is connected through NRF!          
        {
          if (NRF_debug) Serial.println("2.4G");
          NRF_single_target=ES->DestinationUnit;
          NRF_send(ES);
          NRF_single_target=0;
        }
      else
        {
          if (NRF_debug) Serial.println("433M");  
          RawSendRF();
        }
    }
  else
    { // normal transmission, send to both radio's
      RawSendRF();
      NRF_send(ES);
    }
  #else
    // Nodo Small
    NRF_send(ES);
  #endif

  #if NRF_DEBUG
  if (NRF_debug)
    {
      Serial.print("T ");
      Serial.print((int)ES->Type);
      Serial.print(" S ");
      Serial.print((int)ES->SourceUnit);
      Serial.print(" D ");
      Serial.print((int)ES->DestinationUnit);
      Serial.print(" C ");
      Serial.print((int)ES->Command);
      Serial.print(" F ");
      Serial.println((int)ES->Flags);
    }
  #endif

}


void NRF_send(struct NodoEventStruct *event)
{
  ethershield_off();
  
  // this is needed for NRFExtender at the other end (passthrough on I2C)
  struct NodoEventStruct TempEvent;
  memcpy((byte*)&TempEvent, (byte*)event,sizeof(struct NodoEventStruct));
  TempEvent.Port=VALUE_SOURCE_RF;
  TempEvent.Direction=VALUE_DIRECTION_OUTPUT;
  Checksum(&TempEvent);
  memcpy((byte*)&NRFPayload+4, (byte*)&TempEvent,sizeof(struct NodoEventStruct));

  byte first=1;
  byte last=NRF_UNIT_MAX;

  // reset target if flags are 0
  if (event->Flags ==0)
    NRF_single_target = 0;

  if (NRF_single_target > 0)
    {
      first=NRF_single_target;
      last=NRF_single_target;
    }
    
  if (NRF_forceSingleTarget > 0)
    {
      first=NRF_forceSingleTarget;
      last=NRF_forceSingleTarget;
    }

  for(int y=first;y<=last;y++)
    {
      if(NRFOnline[y])
        {
          NRF_sendpacket(Settings.Unit, y, NRF_PAYLOAD_NODO, sizeof(struct NodoEventStruct));
        }    
    }
  
  ethershield_on();
}


void NRF_CheckOnline()
{
  Nrf24_flushRx();
  #if NRF_DEBUG
    if (NRF_debug) Serial.println("NRF Peers Online:");
  #endif
  for(int y=1;y<=NRF_UNIT_MAX;y++)
    {
      if ((NRF_sendpacket(Settings.Unit, y, NRF_PAYLOAD_ONLINE, 0) & 0x20) == 0x20)
        {
          #ifdef NODO_TINY
            SoftwareSerial_print(int2str(y));
            SoftwareSerial_println(" Online");
          #endif
          #if NRF_DEBUG
          if (NRF_debug)
            {
              Serial.print((int)y);
              Serial.println(" is Online");
            }
          #endif
          NRFOnline[y]=true;
          #ifndef NODO_TINY
            NodoOnline(y,VALUE_SOURCE_RF);
          #endif
        }
    }
}

byte NRF_sendpacket(byte Source, byte Destination, byte ID, byte Size)
{
  if (!NRF_live) return 0;
  NRFPayload.Source=Source;
  NRFPayload.Destination=Destination;
  NRFPayload.ID=ID;
  NRFPayload.Size=Size;
  
  NRF_address[0]=Destination;
  NRF_status=0;
  Nrf24_setTADDR((byte *)NRF_address);
  Nrf24_send((byte *)&NRFPayload);
  while(Nrf24_isSending()) {}

  #if NRF_DEBUG      
  if (NRF_debug)
    {
      Serial.print("NRF Send:");
      Serial.print((int)Destination);
      Serial.print(" - ");
      Serial.println((int)NRF_status);
    }
  #endif

  // set auto-ack receive pipe to null
  NRF_address[0]=0;
  Nrf24_setTADDR((byte *)NRF_address);

  delay(NRF_sendDelay);

  return NRF_status;
}

void ethershield_off(void)
{
  #if NODO_MEGA
    digitalWrite(EthernetShield_CS_W5100, 1);
    digitalWrite(EthernetShield_CS_SDCard, 1);
  #endif
}

void ethershield_on(void)
{
  #if NODO_MEGA
    digitalWrite(EthernetShield_CS_W5100, 0);
    digitalWrite(EthernetShield_CS_SDCard, 1);
  #endif
}


// ***********************************************************************************************************
// NRF24 specific code
// ***********************************************************************************************************

/*
    Copyright (c) 2007 Stefan Engelke <mbox@stefanengelke.de>
    Nathan Isburgh <nathan@mrroot.net>
    An Ardunio port of http://www.tinkerer.eu/AVRLib/nRF24L01
    Significant changes to remove depencence on interupts and auto ack support: Aaron Shrimpton <aaronds@gmail.com>

    Permission is hereby granted, free of charge, to any person 
    obtaining a copy of this software and associated documentation 
    files (the "Software"), to deal in the Software without 
    restriction, including without limitation the rights to use, copy, 
    modify, merge, publish, distribute, sublicense, and/or sell copies 
    of the Software, and to permit persons to whom the Software is 
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be 
    included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
    DEALINGS IN THE SOFTWARE.
*/

/* Memory Map */
#define CONFIG      0x00
#define EN_RXADDR   0x02
#define SETUP_AW    0x03
#define RF_CH       0x05
#define STATUS      0x07
#define OBSERVE_TX  0x08
#define CD          0x09
#define RX_ADDR_P0  0x0A
#define RX_ADDR_P1  0x0B
#define RX_ADDR_P2  0x0C
//#define RX_ADDR_P3  0x0D
//#define RX_ADDR_P4  0x0E
//#define RX_ADDR_P5  0x0F
#define TX_ADDR     0x10
#define RX_PW_P0    0x11
#define RX_PW_P1    0x12
#define RX_PW_P2    0x13
//#define RX_PW_P3    0x14
//#define RX_PW_P4    0x15
//#define RX_PW_P5    0x16
#define FIFO_STATUS 0x17

/* Bit Mnemonics */
#define MASK_RX_DR  6
#define MASK_TX_DS  5
#define MASK_MAX_RT 4
#define EN_CRC      3
#define CRCO        2
#define PWR_UP      1
#define PRIM_RX     0
//#define ENAA_P5     5
//#define ENAA_P4     4
//#define ENAA_P3     3
//#define ENAA_P2     2
//#define ENAA_P1     1
//#define ENAA_P0     0
//#define ERX_P5      5
//#define ERX_P4      4
//#define ERX_P3      3
//#define ERX_P2      2
//#define ERX_P1      1
//#define ERX_P0      0
#define AW          0
#define ARD         4
#define ARC         0
#define PLL_LOCK    4
#define RF_DR       3
#define RF_PWR      1
#define LNA_HCURR   0        
#define RX_DR       6
#define TX_DS       5
#define MAX_RT      4
#define RX_P_NO     1
#define TX_FULL     0
#define PLOS_CNT    4
#define ARC_CNT     0
#define TX_REUSE    6
#define FIFO_FULL   5
#define TX_EMPTY    4
#define RX_FULL     1
#define RX_EMPTY    0

/* Instruction Mnemonics */
#define R_REGISTER    0x00
#define W_REGISTER    0x20
#define REGISTER_MASK 0x1F
#define R_RX_PAYLOAD  0x61
#define W_TX_PAYLOAD  0xA0
#define FLUSH_TX      0xE1
#define FLUSH_RX      0xE2
#define REUSE_TX_PL   0xE3
#define NOP           0xFF

void Nrf24_setChannel(byte channel)
{
  Nrf24_configRegister(RF_CH,channel*2);
  delayMicroseconds(256);
}

void Nrf24_transferSync(uint8_t *dataout,uint8_t *datain,uint8_t len)
{
	uint8_t i;
	for(i = 0;i < len;i++){
                #if NRF_SOFT_SPI
		  datain[i] = SPI_transfer(dataout[i]);
                #else
		  datain[i] = SPI.transfer(dataout[i]);
                #endif
	}
}

void Nrf24_transmitSync(uint8_t *dataout,uint8_t len)
{
	uint8_t i;
	for(i = 0;i < len;i++){
                #if NRF_SOFT_SPI
		  SPI_transfer(dataout[i]);
                #else
		  SPI.transfer(dataout[i]);
                #endif
	}
}


void Nrf24_init() 
// Initializes pins to communicate with the MiRF module, should be called in the early initializing phase at startup.
{   
    #if !NRF_SKIP_CE_PIN
      pinMode(NRF_CE_PIN,OUTPUT);
    #endif
    pinMode(NRF_CSN_PIN,OUTPUT);

    Nrf24_ceLow();
    Nrf24_csnHi();

}

void Nrf24_config() 
// Sets the important registers in the MiRF module and powers the module in receiving mode. NB: channel and payload must be set now.
{
    // Set RF channel
    Nrf24_setChannel(Nrf24_channel);

    // Set length of incoming payload 
    Nrf24_configRegister(RX_PW_P0, NRF_PAYLOAD_SIZE);
    Nrf24_configRegister(RX_PW_P1, NRF_PAYLOAD_SIZE);
    Nrf24_configRegister(RX_PW_P2, NRF_PAYLOAD_SIZE);

    // Start receiver 
    Nrf24_powerUpRx();
    Nrf24_flushRx();
}

void Nrf24_setRADDR(uint8_t * adr) 
// Sets the receiving address
{
	Nrf24_ceLow();
	Nrf24_writeRegister(RX_ADDR_P1,adr,mirf_ADDR_LEN);
        byte broadcast=255;
	Nrf24_writeRegister(RX_ADDR_P2,&broadcast,1);
	Nrf24_ceHi();
}

void Nrf24_setTADDR(uint8_t * adr)
// Sets the transmitting address
{
	Nrf24_writeRegister(RX_ADDR_P0,adr,mirf_ADDR_LEN);
	Nrf24_writeRegister(TX_ADDR,adr,mirf_ADDR_LEN);
}

bool Nrf24_dataReady() 
// Checks if data is available for reading
{
    return !Nrf24_rxFifoEmpty();
}

bool Nrf24_rxFifoEmpty(){
	uint8_t fifoStatus;

	Nrf24_readRegister(FIFO_STATUS,&fifoStatus,sizeof(fifoStatus));
	return (fifoStatus & (1 << RX_EMPTY));
}



void Nrf24_getData(uint8_t * data) 
// Reads payload bytes into data array
{
    Nrf24_csnLow();                               // Pull down chip select
    #if NRF_SOFT_SPI
      SPI_transfer( R_RX_PAYLOAD );            // Send cmd to read rx payload
    #else
      SPI.transfer( R_RX_PAYLOAD );            // Send cmd to read rx payload
    #endif
    Nrf24_transferSync(data,data,NRF_PAYLOAD_SIZE); // Read payload
    Nrf24_csnHi();                               // Pull up chip select
    Nrf24_configRegister(STATUS,(1<<RX_DR));   // Reset status register
}

void Nrf24_configRegister(uint8_t reg, uint8_t value)
// Clocks only one byte into the given MiRF register
{
    Nrf24_csnLow();
    #if NRF_SOFT_SPI
      SPI_transfer(W_REGISTER | (REGISTER_MASK & reg));
      SPI_transfer(value);
    #else
      SPI.transfer(W_REGISTER | (REGISTER_MASK & reg));
      SPI.transfer(value);
    #endif
    Nrf24_csnHi();
}

void Nrf24_readRegister(uint8_t reg, uint8_t * value, uint8_t len)
// Reads an array of bytes from the given start position in the MiRF registers.
{
    Nrf24_csnLow();
    #if NRF_SOFT_SPI
      SPI_transfer(R_REGISTER | (REGISTER_MASK & reg));
    #else
      SPI.transfer(R_REGISTER | (REGISTER_MASK & reg));
    #endif
    Nrf24_transferSync(value,value,len);
    Nrf24_csnHi();
}

void Nrf24_writeRegister(uint8_t reg, uint8_t * value, uint8_t len) 
// Writes an array of bytes into inte the MiRF registers.
{
    Nrf24_csnLow();
    #if NRF_SOFT_SPI
      SPI_transfer(W_REGISTER | (REGISTER_MASK & reg));
    #else
      SPI.transfer(W_REGISTER | (REGISTER_MASK & reg));
    #endif
    Nrf24_transmitSync(value,len);
    Nrf24_csnHi();
}


void Nrf24_send(uint8_t * value) 
// Sends a data package to the default address. Be sure to send the correct amount of bytes as configured as payload on the receiver.
{
    uint8_t status;
    status = Nrf24_getStatus();

    while (Nrf24_PTX) {
	    status = Nrf24_getStatus();

	    if((status & ((1 << TX_DS)  | (1 << MAX_RT)))){
		    Nrf24_PTX = 0;
		    break;
	    }
    }                  // Wait until last packet is send

    Nrf24_ceLow();
    #if NRF_SKIP_CE_PIN
      Nrf24_configRegister(CONFIG, mirf_CONFIG ); // power down trick to workaround CE pin tied high!
    #endif
    Nrf24_powerUpTx();       // Set to transmitter mode , Power up
    
    Nrf24_csnLow();                    // Pull down chip select
    #if NRF_SOFT_SPI
      SPI_transfer( FLUSH_TX );     // Write cmd to flush tx fifo
    #else
      SPI.transfer( FLUSH_TX );     // Write cmd to flush tx fifo
    #endif
    Nrf24_csnHi();                    // Pull up chip select
    
    Nrf24_csnLow();                    // Pull down chip select
    #if NRF_SOFT_SPI
      SPI_transfer( W_TX_PAYLOAD ); // Write cmd to write payload
    #else
      SPI.transfer( W_TX_PAYLOAD ); // Write cmd to write payload
    #endif

    Nrf24_transmitSync(value,NRF_PAYLOAD_SIZE);   // Write payload
    Nrf24_csnHi();                    // Pull up chip select

    Nrf24_ceHi();                     // Start transmission
}


bool Nrf24_isSending()
{
	uint8_t status;
	if(Nrf24_PTX){
		status = Nrf24_getStatus();

		// if sending successful (TX_DS) or max retries exceded (MAX_RT).

		if((status & ((1 << TX_DS)  | (1 << MAX_RT)))){
			Nrf24_powerUpRx();
                        NRF_status = status;
			return false; 
		}

		return true;
	}
	return false;
}

uint8_t Nrf24_getStatus()
{
	uint8_t rv;
	Nrf24_readRegister(STATUS,&rv,1);
	return rv;
}

void Nrf24_powerUpRx()
{
	Nrf24_PTX = 0;
	Nrf24_ceLow();
	Nrf24_configRegister(CONFIG, mirf_CONFIG | ( (1<<PWR_UP) | (1<<PRIM_RX) ) );
	Nrf24_ceHi();
	Nrf24_configRegister(STATUS,(1 << TX_DS) | (1 << MAX_RT)); 
}

void Nrf24_flushRx()
{
    Nrf24_csnLow();
    #if NRF_SOFT_SPI
      SPI_transfer( FLUSH_RX );
    #else
      SPI.transfer( FLUSH_RX );
    #endif
    Nrf24_csnHi();
}

void Nrf24_powerUpTx()
{
	Nrf24_PTX = 1;
	Nrf24_configRegister(CONFIG, mirf_CONFIG | ( (1<<PWR_UP) | (0<<PRIM_RX) ) );
}

void Nrf24_ceHi()
{
#if !NRF_SKIP_CE_PIN
	digitalWrite(NRF_CE_PIN,HIGH);
#endif
}

void Nrf24_ceLow()
{
#if !NRF_SKIP_CE_PIN
	digitalWrite(NRF_CE_PIN,LOW);
#endif
}

void Nrf24_csnHi()
{
	digitalWrite(NRF_CSN_PIN,HIGH);
}

void Nrf24_csnLow()
{
	digitalWrite(NRF_CSN_PIN,LOW);
}

void Nrf24_powerDown()
{
	Nrf24_ceLow();
	Nrf24_configRegister(CONFIG, mirf_CONFIG );
}

// Software SPI routines

void SPI_begin()
{
  pinMode(NRF_SCK_PIN,OUTPUT);
  pinMode(NRF_MOSI_PIN,OUTPUT);
  pinMode(NRF_MISO_PIN,INPUT);
  digitalWrite(NRF_SCK_PIN,LOW);
  digitalWrite(NRF_MOSI_PIN,HIGH);
}

unsigned char SPI_transfer(unsigned char Byte)
{
  uint8_t mosibit = digitalPinToBitMask(NRF_MOSI_PIN);
  uint8_t mosiport = digitalPinToPort(NRF_MOSI_PIN);
  uint8_t sckbit = digitalPinToBitMask(NRF_SCK_PIN);
  uint8_t sckport = digitalPinToPort(NRF_SCK_PIN);
  uint8_t misobit = digitalPinToBitMask(NRF_MISO_PIN);
  uint8_t misoport = digitalPinToPort(NRF_MISO_PIN);
  volatile uint8_t *mosiout = portOutputRegister(mosiport);
  volatile uint8_t *sckout = portOutputRegister(sckport);
  volatile uint8_t *misoin = portInputRegister(misoport);
  uint8_t oldSREG = SREG;

  cli();

  for(unsigned char i=0;i<8;i++)
  {
    if(Byte&0x80)
	*mosiout |= mosibit;
    else
	*mosiout &= ~mosibit;

    *sckout |= sckbit;

    Byte <<= 1;
    if (*misoin & misobit)
      Byte |= 1;

    *sckout &= ~sckbit;
  }
  SREG = oldSREG;
  return(Byte);
}

#endif
